
package jpave.annotator;
/**
 * Provides ORF annotations and enters values into database (for table display of ORFs)
 * Runs CCS selfblast and tselfblast for contig comparisons
 * Runs CCS against UniProt for contig comparisons and database BestMatch
 */

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.TreeMap;
import java.util.HashMap;
import java.util.Vector;
import java.util.ArrayList;

import util.algorithms.PairwiseAligner;
import util.methods.TimeHelpers;
import util.Debug.ErrorReport;
import jpave.database.CoreDBWrapper;
import jpave.dataholders.ContigData;
import jpave.dataholders.AlignmentData;
import jpave.dataholders.BlastHitData;
import jpave.annotator.CoreMain;
import jpave.annotator.DoBlast;
import jpave.annotator.DoUniProt;

public class CoreAnnotator {
	// NCBI does not demand a start and stop, but it does demand 100 bases
	private final int ORF_MIN_CUTOFF = 100;
	private final int ORF_BEST_CUTOFF = 600;
	private final int NO_RF = 0;
	private final String orfFileName = "ORF.txt";
	
	final private int MIN_ALIGN_LEN = 20;
	final private int RECORDS_PER_SAVE = 100; 
	final private int RECORDS_PER_COMMIT = 1000;
		
	private boolean bFirstAnno = false;
	private boolean bIsProteinDB = false;
	private boolean bReAnnotate = false;
	private boolean bDoProteinORF=false;
	
	CoreAnnotator () {}
	
	public void setMainObj(DoUniProt db, DoBlast b, CoreDBWrapper s) {
		uniObj = db;
		blastObj = b;
		sqlObj = s;
	}
	
	public boolean run(String projPath) {
		
		try {					
			bFirstAnno=sqlObj.isFirstAnno();	
			bIsProteinDB=sqlObj.isProteinDB();
			bReAnnotate = sqlObj.isReAnnotate();
			
			/*****************************************************************
			  Step 1: preparation
			*******************************************************************/	
			
			CoreMain.StartTime("Prepare for annotation");
			
			// set bogus date to signify start
			if (!setAnnotationDate("'1111-11-11'"))
					return false;

			// Load all of the contigs into the map 
			TreeMap <String, Integer> ctgMap = sqlObj.loadContigMap(); // ctgName, ctgID
			HashMap <Integer, String> annoMap = new HashMap <Integer, String> (); // ctgID, protein rf:start:end
	
			CoreMain.PrtSpMsg(1, ctgMap.size() + " sequences loaded");
			CoreMain.EndTime("Complete preparation");
			
			/*****************************************************************
			   Step 2: Annotate contigs    
			*******************************************************************/
	
			CoreMain.StartTime("Start annotating contigs");
			sqlObj.dropViewTable();
			sqlObj.dropGOTables();
			
			if (blastObj.numDB() > 0) 
			{
				// returns list of contigs with new protein hits
				annoMap = uniObj.processAllDBblastFiles(ctgMap, sqlObj);	
			}

			if (!bIsProteinDB) {
				String orfPathName = projPath + "/" + orfFileName;
				// only annotate contigs with new proteins for protein ORF
				if (!bFirstAnno && !bReAnnotate && blastObj.numDB() > 0){
					findAllORFs(ctgMap, annoMap, false, orfPathName); // only annotated contigs
				}
				else if (bFirstAnno || bReAnnotate) {
					findAllORFs(ctgMap, annoMap, true, orfPathName); // all contigs
				}
				else {} // doing more pairs
			}
			
			System.err.print("                                                                         \r");
			CoreMain.EndTime("\nFinished annotating contigs");
			
			/*******************************************************************
			   Step 3. Run homology tests on contigs  (calls getORFcoding)
			********************************************************************/
			
			if (!setAnnotationDate("'1111-11-12'")) return false;
			
			if (!doHomologyTests()) return false;
			  
			if (!setAnnotationDate(null)) return false;

			return true;
		} catch (Throwable e) {
			ErrorReport.reportError(e, "Core Annotator");
			return false;
		}
	}

	private boolean setAnnotationDate(String date) {
		try {
			sqlObj.setAnnotationDate(date);
		} catch (Exception e) {
			ErrorReport.reportError(e, "Failed to set annotation date.");
			return false;
		}
		return true;
	}
	
	/***************** HOMOLOGY TESTS *********************************/
	/**
	 * The UniProt blast file has been read, loaded and committed
	 * 1. uniProtPairs Get all UniProt hits from database that occur in two contigs
	 * 2. selfBlastPairs Read selfblast file for all megablast hits
	 * 3. tselfBlastPairs Read tselfblast file for all tblastx hits
	 * 4. pairsSpMx contains all pairs
	 * 5. Get all current pair_wise data and remove from pairsSpMx so not redone
	 * 7. DP all pairs within JPAVE_pairs_limit 
	 */
	private boolean doHomologyTests() throws Exception {
								
		if (maxDPpairs == 0 || !blastObj.doPairs()) {
			CoreMain.PrtSpMsg(0, "No sequence comparisons to be performed");
			return true;
		}
		if (bIsProteinDB) {
			CoreMain.PrtSpMsg(0, "This option is disable for protein databases");
			return true;
		}
		
		CoreMain.StartTime("Start sequence comparisons");
		if (!blastObj.runSelfBlast("megablast")) return false;		
		if (!blastObj.runSelfBlast("tblastx")) return false;
		
		CoreMain.PrtSpMsg(1, "Find pairs to align");
		// used for updating existing pairs where key is ctg1;ctg2
		HashMap <String, BlastHitData> pairsHash = 	new HashMap <String, BlastHitData> ();
		// pairs copied for sorting 
		ArrayList <BlastHitData> pairsList = new ArrayList <BlastHitData>(); 
		int cnt1, cnt2;
	
		/******************************************************************
		 * Load pairs from UniTrans selfblast and UniTrans tselfblast in  matrix
		 *********************************************************************/

		// tblastx: Load pairs from translated self-blast file
		cnt1=cnt2=0;
		String fileTransSelfBlast = blastObj.getTSelfBlastFile();
		if (fileTransSelfBlast != null && !fileTransSelfBlast.equals("-")) {
			cnt2 =  blastObj.getAllPairsFromBlastFile(pairsHash,
						fileTransSelfBlast, false /* !isSelf */);
			prtline("Pairs from tblastx self-blast ", cnt2);
		}
		
		// megablast: Load pairs from self-blast file
		String fileSelfBlast = blastObj.getSelfBlastFile();		
		if (fileSelfBlast != null && !fileSelfBlast.equals("-")) {
			cnt1  = blastObj.getAllPairsFromBlastFile(pairsHash, 
					fileSelfBlast, true /* isSelf */);
			if (cnt2==0) prtline("Pairs from self-blast ", cnt1);
			else prtline("Additional pairs from self-blast ", cnt1);
		}
	    
		if (cnt1==0 && cnt2==0) {
			CoreMain.PrtSpMsg(0, "No pairs to align");
			return true;
		}
		if (!bFirstAnno) {
			cnt1 = sqlObj.removePairsInDB (pairsHash);
			prtline("Total unique pairs after removing " + cnt1 + " existing pairs", pairsHash.size());
		}
		else prtline("Total unique pairs ", pairsHash.size());
	
		/************************************************************   
		 *  Make final list
		 *************************************************************/
		
	    for ( String key : pairsHash.keySet() )
        {
	    		BlastHitData hit = pairsHash.get( key );
	    		pairsList.add(hit);
        }
		
		Collections.sort(pairsList);
		pairsHash.clear();
		
		/************************************************************   
		 *  XXX  Align all of the sequences
		 *************************************************************/
		PairwiseAligner dpAlgo = new PairwiseAligner();
		Vector <AlignmentData> alignmentList = new Vector<AlignmentData>();
		HashMap <String, Integer> cntPairsCtgHash = new HashMap <String, Integer> ();
		
		int nPairs = pairsList.size();
		int failToAlign=0, tooShort=0;
		int successToAlign=0;
		int nCompareCount = 0;
		BlastHitData hit = null;
		
		if (maxDPpairs < nPairs) {
			CoreMain.PrtSpMsg(1, "Aligning best " + maxDPpairs + " out of " + nPairs 
					+ " pairs, due to Pairs limit in Options");
			nPairs = maxDPpairs;
		}
		else 
			CoreMain.PrtSpMsg(1, "Aligning " + nPairs + " pairs");
		
		for (int i = 0; i < nPairs && successToAlign < maxDPpairs; ++i) {		

			hit = pairsList.get(i);
			if (hit.getAlignLen() < MIN_ALIGN_LEN) {
				tooShort++;
				continue; // XXX heuristic
			}
			
			String ctgID1 = hit.getContigID();
			String ctgID2 = hit.getHitID();
			
			// a sequence may be loaded many times... possibly save in hashMap
			ContigData contig1 = sqlObj.loadContigData(ctgID1);
			ContigData contig2 = sqlObj.loadContigData(ctgID2);
			
			++nCompareCount;
			if (nCompareCount % 50 == 0) {
				System.err.print("   Compared " + nCompareCount + " pairs "	+ (nPairs - nCompareCount) + " remain\r");
			}
				
			// XXX Do the Alignment		
			AlignmentData theAlign = AlignmentData.pairAlignCore(
						dpAlgo, contig1, contig2, hit,
						AlignmentData.firstResult);
				
			if (theAlign  == null) { // fails on out of memory
				System.err.println("Cannot align " + 
						contig1.getContigID() + " (" + contig1.getSeqData().getLength() + "bp) " +
						contig2.getContigID() + " (" + contig2.getSeqData().getLength() + "bp)"  );
				failToAlign++;
				continue;
			}

			successToAlign++;
			
			if (!bFirstAnno) sqlObj.loadSharedHitsCnt(hit);
			
			// Add to the list, save if we have enough
			theAlign.releaseSequences();
			alignmentList.add(theAlign);
			
			if (alignmentList.size() >= RECORDS_PER_SAVE) {				
				sqlObj.savePairwiseAlignments(CoreMain.getAssemblyID(), alignmentList);
				alignmentList.removeAllElements();
			}

			if (successToAlign % RECORDS_PER_COMMIT == 0) {
				sqlObj.commitChanges();
			}
			
			updateHash(contig1.getContigID(), cntPairsCtgHash);
			updateHash(contig2.getContigID(), cntPairsCtgHash);
		}

		sqlObj.savePairwiseAlignments(CoreMain.getAssemblyID(), alignmentList);
		sqlObj.commitChanges();
		sqlObj.saveAllCtgPairwiseCnts(cntPairsCtgHash);
		System.err.print("                                                                                    \r");
		
		if (failToAlign > 0) prtline("Failed alignments", failToAlign);
		if (tooShort > 0)    prtline("Rejected alignments (<" + MIN_ALIGN_LEN + "bp)", tooShort);
		int x = AlignmentData.cntNewBest;
		if (x >0) prtline("found one better the blast frame",x);
		CoreMain.EndTime("Finished " + successToAlign + " contig comparisons");
		return true;
	}

	private void updateHash(String ctgName, HashMap <String, Integer> map) {
		if (map.containsKey(ctgName)) {
			int cnt = map.get(ctgName) + 1;		
			map.put(ctgName, cnt);
		}
		else map.put(ctgName, 1);
	}
	void prtline(String msg, int cnt) {
		String t = String.format("%6d ", cnt);
		CoreMain.PrtSpMsg(1, t + msg);
	}
	/***************************************************************
	 * CAS 2/13/15 optimized GC/ORF routines
	 */
	
	public void findAllORFs ( TreeMap<String, Integer> ctgMap, 
			HashMap <Integer, String> annoMap, boolean allCtgs, String orfPath) 
    {
        try {   
          	long time = TimeHelpers.getTime();
          	CoreMain.PrtSpMsg(1, "");
			if (allCtgs) CoreMain.PrtDateMsg("Annotate all sequences with GC and ORF");
			else CoreMain.PrtDateMsg("Annotate protein hit sequences with ORF");
			int nAnnoCtg=0;
			
			CoreMain.PrtSpMsg(1, "Writing ORF coordinates into " + orfPath);
			orfFile = new BufferedWriter(new FileWriter(orfPath, false));
			orfFile.write(String.format(" %15s %5s %5s %5s %5s %6s %7s %10s\n", 
				"", "Frame", "Len", "Start", "End", "HasATG", "HasStop", "Protein confirmation"));
			
			// initialize to compute and commit 100 at a time
			int numSeq = (ctgMap.size()>100) ? 100 : ctgMap.size();
			seqData = new SeqData [numSeq];
			for (int i=0; i<numSeq; i++) seqData[i] = new SeqData();
			
			dbc = sqlObj.getConn();
			stmt = dbc.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE );
         
			int n=0;
            for (String ctgName : ctgMap.keySet()) { 
            		int ctgID = ctgMap.get(ctgName);
        
            		String rfStr = (annoMap.containsKey(ctgID)) ? annoMap.get(ctgID) : "";
            		if (!allCtgs && rfStr.equals("")) continue;
            		
                loadContigDataForORF(n, ctgID, ctgName);   
                if (seqData[n].hasNs==1) continue;
               
            // GC
                String seq = seqData[n].seq;
                int nGCcount=0;
                for (int i = 0; i < seq.length(); i++) {
        				char c = seq.toUpperCase().charAt(i);
        				if (c == 'G' || c == 'C') nGCcount++;
                }
        			seqData[n].gcRatio = (double) nGCcount/ (double) seq.length();
        			
        		// ORF	
        			int prf=NO_RF, pstart=0, pend=0;
        			double peval=0.0;
        			if (!rfStr.equals("")) {
        				String [] x = rfStr.split(":");
        				if (x.length != 4) CoreMain.die("rfStr: " + rfStr);
        				prf = Integer.parseInt(x[0]);
        				pstart = Integer.parseInt(x[1]);
        				pend = Integer.parseInt(x[2]);
        				peval = Double.parseDouble(x[3]);
        			}
        			OrfData orf = findORF_best(seqData[n], prf, pstart, pend, peval);
		        
		        seqData[n].frame = orf.frame;
		        seqData[n].nBegin = orf.nBegin;
		        seqData[n].nEnd = orf.nEnd;
		        seqData[n].hasBegin = orf.hasBegin;
		        seqData[n].hasEnd = orf.hasEnd;
		        seqData[n].anno = orf.anno;
		        n++;
		        if (n==numSeq) {
		        		saveContigORFData (n);  
		        		n=0;
		        }
		       
                ++nAnnoCtg;
            		if (nAnnoCtg % 100 == 0) System.err.print("      Finished " + nAnnoCtg  
        				+ " sequences\r");
            }
            if (n>0) {
            		saveContigORFData (n); 
            		sqlObj.commitChanges ( );  
            }
            stmt.close();
            orfFile.close();
          	CoreMain.PrtSpMsgTime(2, nAnnoCtg + 
          			" total sequences annotated with ORF and GC           Total time: ", time);
        } 
        catch (Exception err) {
			ErrorReport.reportError(err, "Annotator - process remaining");
        }
    }
	
	/*****************************************************
	 * XXX Check all reading frames for the best
	 * Though protein ORF is computed separately, the whole set is computed for output
	 */
	private OrfData findORF_best(SeqData seqData, int prf, int pstart, int pend, double peval) {
	try {
		ORFs.clear();
		
		for (int i = -3; i <= 3; ++i) {
			if (i == 0) continue;
			
			OrfData o = findORF_rf(seqData, i, prf); 
			if (o!=null) ORFs.add(o);
		}
		if (ORFs.size()==0) return new OrfData(0,0,0,false,false, false, false); 
		Collections.sort(ORFs);
		OrfData bestorf = ORFs.get(0);
		if (bestorf.frame == prf) bestorf.anno=1;
		
		// write to file
		orfFile.write(String.format(">%15s len=%d\n", seqData.name, seqData.seqLen));
		for (OrfData o : ORFs) {
			String rev="";
			if (o.frame <0) rev = String.format("(%d..%d)", 
					(seqData.seqLen-o.nEnd+1), (seqData.seqLen-o.nBegin+1) );
			orfFile.write(String.format(" %15s %5d %5d %5d %5d %6b %7b %10b %s\n", 
				"", o.frame, o.len, o.nBegin, o.nEnd, o.hasBegin, o.hasEnd, o.hasAnno, rev));
		}
		return bestorf;
	} catch (Exception e) {ErrorReport.reportError("Error in ORF finder", e); return null;}
	}

	/************************************************
	 * A sequence may have multiple ORFs, so there are heuristics to determine best one
	 */
	private OrfData findORF_rf(SeqData seqData, int frame, int prf) {
		int seqLen = seqData.seqLen;
		String strSeq = seqData.seq;
		if (frame < 0) strSeq = seqData.seqRev;
 
		int arf = Math.abs(frame);
		int bestATG = 0, bestSTOP = 0, bestLen = 0; ;
		int firstSTOP = 0;
		int nATG = -1, nSTOP = 0, nLen = 0;
		
		for (int i = arf - 1; i < (seqLen-2); i += 3) {
			if (nATG < 0 && codon(strSeq, i, "ATG")) { // first and after each STOP nStart < 0
				nATG = i + 1;
			}
			// first STOP defines first ORF (maybe ATG in between), nATG > 0 has found an ATG
			else if ((firstSTOP == 0 || nATG > 0) &&	codonStop(strSeq, i))
			{
				nSTOP = i + 3;
				nLen = nSTOP - nATG + 1;
				if (nATG > 0 && bestLen < nLen) {
					bestATG = nATG;
					bestSTOP = nSTOP;
					bestLen = nLen;			
				}
				if (firstSTOP == 0) firstSTOP = nSTOP; // end to first STOP
				nATG = -1;   // find next ATG
			}
		}
	
		// Take the ATG-STOP or ATG-END or END-STOP
		boolean hasAnno = (frame == prf) ? true : false;
		
		int atgLen=0, stopLen=0;	
		if (nATG>=0) atgLen = seqLen - nATG + 1;
		if (firstSTOP!=0) stopLen = firstSTOP - arf + 1;
		
		if (bestLen > ORF_BEST_CUTOFF) 
			return new OrfData(bestATG, bestSTOP, frame, true, true, hasAnno, bestLen==seqData.seqLen);
		
		if (bestLen > ORF_MIN_CUTOFF && bestLen > atgLen && bestLen > stopLen) 
			return new OrfData(bestATG, bestSTOP, frame, true, true, hasAnno, bestLen==seqData.seqLen);
		
		if (firstSTOP==0) {// no stop, take whole thing
			boolean hasATG = (codon(strSeq,arf-1, "ATG)")) ? true : false;
			return new OrfData(arf, seqLen, frame, hasATG, false, hasAnno, true);
		}
		if (atgLen > ORF_MIN_CUTOFF && atgLen > stopLen) 
			return new OrfData(nATG, seqLen, frame, true, false, hasAnno, atgLen==seqData.seqLen);
		
		if (stopLen > ORF_MIN_CUTOFF) 
			return new OrfData(arf, firstSTOP, frame, false, true, hasAnno, stopLen==seqData.seqLen);
	
		return null;
	}
	// TAA, TAG, TGA
	static private boolean codonStop(String strSeq, int n) {
		if (strSeq.charAt(n) == 'T') {
			if (strSeq.charAt(n+1) == 'A') {
				if (strSeq.charAt(n+2) == 'A') return true;
				else if (strSeq.charAt(n+2) == 'G') return true;
				else return false;
			}
			else if (strSeq.charAt(n+1) == 'G') {
				if (strSeq.charAt(n+2) == 'A') return true;
				else return false;
			}
			else return false;
		}
		return false;
	}
	static private boolean codon ( String str, int nStart, String strSub )
    {
       if ( str.charAt(nStart) != strSub.charAt(0) )return false;
       if ( str.charAt(nStart+1) != strSub.charAt(1) )return false;
       if ( str.charAt(nStart+2) != strSub.charAt(2) )return false;
       return true;
    }
	
	// database load
	private void loadContigDataForORF ( int n, int ctgID, String ctgName ) 
   {
       ResultSet rs = null;  
       try
       {
           seqData[n].CTGid = ctgID ;
           seqData[n].name = ctgName;
           
           rs = stmt.executeQuery("SELECT consensus, has_ns " +
       			"FROM contig WHERE contig.CTGID = "  + ctgID); 
           if ( !rs.next() ) 
           	   ErrorReport.die("No sTCW database entry for " + ctgName , null);
 
           String seqString = rs.getString(1);  
           seqData[n].hasNs = rs.getInt(2);
           
           if (seqString.contains("*") || seqString.contains(".")) {
        	   		StringBuilder builder = new StringBuilder ();
        	   		for (int i=0; i<seqString.length(); i++) {
	        	   		char c = seqString.charAt(i);
	        	   		if (c!='*' && c!='.') builder.append(c);
        	   		}
        	   		seqData[n].seq = builder.toString();
           }
           else seqData[n].seq = seqString;
           seqData[n].seqLen = seqData[n].seq.length();
           
           StringBuilder builder = new StringBuilder ();
           for (int i = seqString.length() - 1; i >= 0; --i) {
   			   builder.append(getBaseComplement(seqString.charAt(i)));
   		   }
           seqData[n].seqRev = builder.toString();
           if ( rs != null ) rs.close();
       }
       catch (Exception e) {
	    	   ErrorReport.die(e, "Loading Consensus and Data");
       }
	 }
	static private char getBaseComplement(char chBase) {
		switch (chBase) {
		case 'a': return 't'; case 'A': return 'T'; 
		case 't': return 'a'; case 'T': return 'A';
		case 'c': return 'g'; case 'C': return 'G';
		case 'g': return 'c'; case 'G': return 'C'; 
		case 'N': case 'n': 
		default: return chBase;
		}
	}
	 // database save
	 private void saveContigORFData (int num) 
     {  
		 int frame=0, start=0, end=0;
         try {  
        	    PreparedStatement ps = dbc.prepareStatement(
    					"update contig SET " +
    					"gc_ratio=?, " +
    					"o_coding_start=?, o_coding_end=?, o_frame=?, " +
    					"o_coding_has_begin=?, o_coding_has_end=?, " +
    					"p_coding_has_begin=? where CTGID=?"); // p_coding_start hijacked for anno flag
        	    
        	 	for (int i=0; i<num; i++) {
        	 		ps.setDouble(1, seqData[i].gcRatio);
        	 		if (seqData[i].nBegin==0 && seqData[i].nEnd==0) {
        	 			ps.setInt(2, 0);
        	 			ps.setInt(3, 0);
        	 			ps.setInt(4, 0);
        	 			ps.setInt(5, 0);
        	 			ps.setInt(6, 0);
        	 			ps.setInt(7, 0);
        	 		}
        	 		else {
        	 			if (seqData[i].frame <-3 || seqData[i].frame>3 || seqData[i].nBegin<0 || seqData[i].nEnd<0)
        	 				System.out.println("Internal error: " 
        	 						+ seqData[i].CTGid + " f:" + seqData[i].frame + " s:" +
        	 						+ seqData[i].nBegin  + " e:" + seqData[i].nEnd);
        	 			frame = (seqData[i].frame<-3 || seqData[i].frame>3) ? 0 : seqData[i].frame;
        	 			start = (seqData[i].nBegin<0) ? 0 : seqData[i].nBegin;
        	 			end = (seqData[i].nEnd < 0) ? 0  : seqData[i].nEnd;
        	 			
        	 			int hb = (seqData[i].hasBegin) ? 1 : 0;
      	            int he = (seqData[i].hasEnd) ? 1 : 0; 
      	            int anno = (seqData[i].anno>0) ? 1 : 0;
      	        		ps.setInt(2, start);
        	 			ps.setInt(3, end);
        	 			ps.setInt(4, frame);
        	 			ps.setInt(5, hb);
        	 			ps.setInt(6, he);
        	 			ps.setInt(7,anno);
        	 		}
        	 		ps.setInt(8, seqData[i].CTGid);
        	 		ps.addBatch();
        	 	}
        	 	ps.executeBatch();
         }
 		catch (SQLException e) {
 			ErrorReport.die(e, "cannot save new annotations to database: frame " +
 					frame + " start " + start + " end " + end);
         }
     }

	private class SeqData {
		int CTGid;
		String name;
		String seq, seqRev;
		int seqLen;
		double gcRatio;
		int nBegin, nEnd, frame, anno;
		boolean hasBegin, hasEnd;	
		int hasNs;
	}
	private SeqData [] seqData;
	ArrayList <OrfData> ORFs = new ArrayList <OrfData>();
	private class OrfData implements Comparable<OrfData>{
		public OrfData (int b, int e, int f, boolean hB, boolean hE, boolean hA, boolean iF) {
			nBegin=b;
			nEnd=e;
			frame=f;
			hasBegin=hB;
			hasEnd=hE;
			len = nEnd-nBegin+1;
			hasAnno=hA;
			anno = (hasAnno) ? 1 : 0;
			isFullLen=iF;
		}
		int nBegin, nEnd, frame, len, anno;
		boolean hasBegin, hasEnd, hasAnno, isFullLen;
		
		public int compareTo(OrfData rORF)
		{
			// for genes
			if (hasBegin && isFullLen && frame==1) return -1;
			if (rORF.hasBegin && rORF.isFullLen && rORF.frame==1) return 1;
			
			if (len > ORF_BEST_CUTOFF && hasAnno) return -1;
			if (rORF.len > ORF_BEST_CUTOFF && rORF.hasAnno) return 1;
			
			if ( len != rORF.len ) return rORF.len - len;
			
			if (hasAnno) return -1;
			if (rORF.hasAnno) return 1;
			
			if ( hasBegin && hasEnd && (!rORF.hasBegin || !rORF.hasEnd )  )
				return -1;
			if ( rORF.hasBegin && rORF.hasEnd && (!hasBegin || !hasEnd )  )
				return 1;	
			
			return 0;
		}
	}
	
	// set in CoreMain - the reps are obsolete
	public void setMaxDPpairs(int i) {maxDPpairs = i;}		
	static int maxDPpairs = 0;
	
	CoreDBWrapper sqlObj = null;
	DoBlast blastObj = null;
	DoUniProt uniObj = null;
	
	Connection dbc = null;
	Statement stmt = null;
	BufferedWriter orfFile = null;
}
